package game.player;

import game.GameGrid;

public class Computer extends Player {
	/*RECURSION need to be at least 4 to discover the most fundamental wins by
	 * the opponent. If RECURSION is on 2 it stops 3 in a row but misses 
	 * 2-in-line with open ends. RECURSION works far better when it is 
	 * an even number >= 4.*/
	public final int RECURSION = 8;
	private GameGrid grid;

	public Computer(String name, int player, int colorID, GameGrid grid) {
		super(name, player, colorID);
		this.grid = grid;
	}
	
	
	/*The getMovesum function starts of by the last disc placed and produces all
	 * possible segments of four that is using that disc. These segments are
	 * valuated by getSegmentValue and the sum of all segments are returned.
	 */
	public int getMoveSumVertical(int row, int coloumn) {
		int[] array = new int[4];
		int sum = 0;
		row = (row - 3 < 0) ? 0 : row - 3;
		for (int i = row, k = 0; k < 4; i++, k++) {
			try {
				array[0] = grid.getGridCell(i, coloumn);
				array[1] = grid.getGridCell(i + 1, coloumn);
				array[2] = grid.getGridCell(i + 2, coloumn);
				array[3] = grid.getGridCell(i + 3, coloumn);
				sum += getSegmentValue(array);
			} catch (ArrayIndexOutOfBoundsException e) {
			}
		}
		return sum;
	}

	public int getMoveSumHorizontal(int row, int coloumn) {
		int[] array = new int[4];
		int sum = 0;
		coloumn -= 3;
		for (int i = coloumn, k = 0; k < 4; i++, k++) {
			try {
				array[0] = grid.getGridCell(row, i);
				array[1] = grid.getGridCell(row, i + 1);
				array[2] = grid.getGridCell(row, i + 2);
				array[3] = grid.getGridCell(row, i + 3);
				sum += getSegmentValue(array);
			} catch (ArrayIndexOutOfBoundsException e) {
			}
		}
		return sum;
	}

	public int getMoveSumDiagonalDown(int row, int coloumn) {
		int[] array = new int[4];
		int sum = 0;
		row -= 3;
		coloumn -= 3;

		for (int i = row, j = coloumn, k = 0; k < 4; i++, j++, k++) {
			try {
				array[0] = grid.getGridCell(i, j);
				array[1] = grid.getGridCell(i + 1, j + 1);
				array[2] = grid.getGridCell(i + 2, j + 2);
				array[3] = grid.getGridCell(i + 3, j + 3);
				sum += getSegmentValue(array);
			} catch (ArrayIndexOutOfBoundsException e) {
			}
		}
		return sum;
	}

	public int getMoveSumDiagonalUp(int row, int coloumn) {
		int[] array = new int[4];
		int sum = 0;
		coloumn -= 3;
		row += 3;

		for (int i = row, j = coloumn, k = 0; k < 4; i--, j++, k++) {
			try {
				array[0] = grid.getGridCell(i, j);
				array[1] = grid.getGridCell(i - 1, j + 1);
				array[2] = grid.getGridCell(i - 2, j + 2);
				array[3] = grid.getGridCell(i - 3, j + 3);
				sum += getSegmentValue(array);
			} catch (ArrayIndexOutOfBoundsException e) {
			}
		}
		return sum;
	}
	
	/*Gets a segment from the game board containing a row of 4. Based on the 
	 segments composition it returns a value where a high point is closer to
	 a winning move.*/
	public int getSegmentValue(int[] row) {
		int count = 0;
		int playerId = 0;
		for (int i = 0; i < row.length; i++) {
			if (row[i] != 0) {
				playerId = row[i];
				break;
			}
		}
		for (int i = 0; i < row.length; i++) {
			if (row[i] != playerId && row[i] != 0) {
				return 0;
			} else if (row[i] == playerId) {
				count++;
			}
		}
		switch (count) {
			case 1:
				return 1;
			case 2:
				return 4;
			case 3:
				return 32;
			case 4:
				return 5000;
			default: //Will never happen.
				return 1;
		}
	}

	@Override
	/*Uses the recursive algorithm to find and choose the best possible move.*/
	public int nextMove() {
		int coloumn = 0, tempSum, sum = Integer.MIN_VALUE;

		for (int i = 0; i < grid.COLUMNS; i++) {
			tempSum = recursiveNextMove(i, RECURSION, Integer.MIN_VALUE, Integer.MAX_VALUE, getPlayer());
			if (tempSum > sum) {
				sum = tempSum;
				coloumn = i;
			}	
		}
		return coloumn;
	}

	private int recursiveNextMove(int move, int depth, int alpha, int beta, int player) {
		int sum;
		if (validMove(move)) {
			grid.placeDisc(move, player);
		} else {
			if (RECURSION == depth)
				return Integer.MIN_VALUE;
			else {
				sum = getMoveSum(move);
				return sum;
			}
		}
		sum = getMoveSum(move);
		
		if (sum > 5000) {
			grid.removeLastDisc(move);
			if (player == getPlayer()) {
				return sum*depth;
			} else {
				return -sum*depth;
			}
		}

		if (depth == 0) {
			grid.removeLastDisc(move);
			return sum;
		}

		if (player != getPlayer()) {
			for (int i = 0; i < grid.COLUMNS; i++) {
				alpha = Math.max(alpha, recursiveNextMove(i, depth - 1, alpha, beta, (player % 2) + 1));
				if (beta <= alpha) {
					break;
				}
			}
			grid.removeLastDisc(move);
			return alpha;
		} else {
			for (int i = 0; i < grid.COLUMNS; i++) {
				beta = Math.min(beta, recursiveNextMove(i, depth - 1, alpha, beta, (player % 2) + 1));
				if (beta <= alpha) {
					break;
				}
			}
			grid.removeLastDisc(move);
			return beta;
		}
	}
	
	/*Sums up all the getMoveSum functions.*/
	private int getMoveSum(int coloumn) {
		int row = grid.findTopDisc(coloumn);
		int sum = 0;
		sum += getMoveSumHorizontal(row, coloumn);
		sum += getMoveSumVertical(row, coloumn);
		sum += getMoveSumDiagonalDown(row, coloumn);
		sum += getMoveSumDiagonalUp(row, coloumn);
		return sum;
	}

	private boolean validMove(int column) {
		return (grid.getGridCell(0, column) == 0) ? true : false;
	}
}